Skip to content

Conversation

@findolor
Copy link
Collaborator

@findolor findolor commented Jan 27, 2026

Motivation

This change adds the ability to completely reset an in-browser SQLite database by wiping all OPFS storage and recreating the worker. This is useful for scenarios where users need a fresh database state without reloading the page.

Solution

Added a new wipe_and_recreate() async method to SQLiteWasmDatabase that:

  1. Terminates the existing worker
  2. Rejects any pending queries with "Database wipe in progress" error
  3. Resets the ready signal to pending state
  4. Deletes the OPFS sahpool directory containing persisted data
  5. Creates a new worker with fresh state
  6. Waits for the new worker to be ready

Supporting changes:

  • Added new opfs.rs module with OPFS directory deletion utilities
  • Added ReadySignal::reset() method to return signal to pending state
  • Added OpfsDeletionFailed error variant
  • Added web-sys features for OPFS access (StorageManager, FileSystemDirectoryHandle, etc.)
  • Changed worker field to Rc<RefCell<Worker>> to allow replacement
  • Added db_name field to store database name for worker recreation
  • Added comprehensive tests for the new functionality
  • Added demo UI button in svelte-test app

Checks

By submitting this for review, I'm confirming I've done the following:

  • made this PR as small as possible
  • unit-tested any new functionality
  • linked any relevant issues or PRs
  • included screenshots (if this involves a front-end change)

Summary by CodeRabbit

  • New Features

    • Added database wipe and recreate functionality with a new UI control to reset and reload the database schema.
  • Tests

    • Introduced comprehensive integration tests validating wipe behavior, data integrity, and CRUD operations after reset.
  • Chores

    • Updated dependencies to support enhanced storage capabilities.

✏️ Tip: You can customize this high-level summary in your review settings.

Features added: StorageManager, FileSystemDirectoryHandle,
FileSystemHandle, FileSystemRemoveOptions, FileSystemGetDirectoryOptions
Allows resetting the signal back to pending state for reuse
after wipe_and_recreate operations.
Provides delete_opfs_sahpool_directory() to remove the .opfs-sahpool
directory used by sqlite-wasm-rs for persistent storage.
Allows completely wiping and recreating the database:
- Terminates current worker
- Rejects pending queries
- Deletes OPFS storage
- Creates fresh worker
- Same instance remains valid after operation

Uses interior mutability (Rc<RefCell<Worker>>) to allow
bound callbacks to continue working.
- 8 integration tests covering data clearing, post-wipe operations,
  instance validity, and error recovery
- Wipe & Recreate button in demo app for manual testing
@findolor findolor self-assigned this Jan 27, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 27, 2026

Walkthrough

Introduces a database wipe and recreate feature for SQLite WASM by refactoring worker management to use reference-counted interior mutability, adding OPFS directory deletion capabilities, extending the ReadySignal with reset functionality, and including comprehensive integration tests and UI controls.

Changes

Cohort / File(s) Summary
Web-sys Feature Configuration
Cargo.toml
Added six new web-sys features: StorageManager, FileSystemDirectoryHandle, FileSystemHandle, FileSystemRemoveOptions, FileSystemGetDirectoryOptions, DomException to workspace dependencies.
SQLiteWasmDatabase Core Refactoring
packages/sqlite-web/src/db.rs
Refactored worker storage from Worker to Rc<RefCell<Worker>> for proper lifecycle management. Added public db_name: String field. Updated query() method to borrow worker via Rc and RefCell. Introduced new wipe_and_recreate() public method that terminates worker, rejects in-flight queries, resets readiness, deletes OPFS directory, creates new worker instance, reattaches handlers, and waits for readiness.
Error Handling & Infrastructure
packages/sqlite-web/src/errors.rs, ready.rs, lib.rs
Added OpfsDeletionFailed(String) error variant. Added reset() method to ReadySignal to clear handlers, reject previous readiness with custom message, and recreate promise in Pending state. Declared new opfs module.
OPFS Cleanup Module
packages/sqlite-web/src/opfs.rs
New 154-line module implementing OPFS cleanup. Exports delete_opfs_sahpool_directory() async function with private helpers for obtaining OPFS root, looking up subdirectories conditionally, deleting directory contents recursively, and collecting entry names via JS iterator interop. Comprehensive error mapping to SQLiteWasmDatabaseError.
Test Suite Refactoring
packages/sqlite-web/src/tests.rs, svelte-test/tests/integration/wipe-and-recreate.test.ts
Removed 210+ lines of legacy WASM tests covering database construction, query formatting, and parameter normalization. Added comprehensive integration test suite validating wipe effects, data persistence, table recreation, CRUD operations post-wipe, parameterized queries, and recovery from corruption.
UI & Package Updates
pkg/package.json, svelte-test/package.json, svelte-test/src/routes/+page.svelte
Updated svelte-test dependency from 0.0.1-alpha.9 to 0.0.1-alpha.20. Added wipeAndRecreate() function in Svelte component with confirmation prompt and status tracking. Added new "Wipe & Recreate DB" button with loading state and styling.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Svelte as Svelte Component
    participant DB as SQLiteWasmDatabase
    participant Worker as Worker Thread
    participant OPFS as OPFS Storage
    participant Ready as ReadySignal

    User->>Svelte: Click "Wipe & Recreate DB"
    Svelte->>Svelte: Show confirmation dialog
    Svelte->>DB: wipeAndRecreate()
    
    DB->>Worker: Terminate current worker
    DB->>DB: Reject in-flight queries
    DB->>Ready: reset()
    Ready->>Ready: Reject with "Ready signal reset"
    
    DB->>OPFS: delete_opfs_sahpool_directory()
    OPFS->>OPFS: Get OPFS root directory
    OPFS->>OPFS: Locate .opfs-sahpool directory
    OPFS->>OPFS: Delete all contents recursively
    OPFS->>OPFS: Remove sahpool directory
    OPFS-->>DB: Success/Error
    
    DB->>Worker: Create new embedded worker instance
    DB->>Worker: Reattach message handler
    DB->>Ready: Wait for readiness
    Worker-->>Ready: mark_ready()
    Ready->>Ready: Transition to Ready state
    
    Ready-->>DB: Ready signal resolved
    DB-->>Svelte: Return success
    Svelte->>Svelte: Recreate schema
    Svelte->>DB: Execute queries
    Svelte-->>User: Display refreshed data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested labels

codex

Suggested reviewers

  • hardyjosh
  • 0xgleb
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add wipe_and_recreate method to SQLiteWasmDatabase' accurately describes the primary feature addition in the pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@findolor findolor requested review from 0xgleb and hardyjosh January 27, 2026 07:50
@findolor
Copy link
Collaborator Author

Code review

Found 1 issue:

  1. Test ready_signal_reset_returns_to_pending violates promise lifecycle contract. At line 173-174, the test calls mark_ready() then expects wait_promise() to succeed. But mark_ready() removes the promise via self.promise.borrow_mut().take() (line 74), and existing test at lines 133-136 documents that "promise should be dropped after resolution". The fix is to get the promise BEFORE calling mark_ready().

signal.mark_ready();
let promise = signal.wait_promise().expect("promise exists after mark_ready");
wasm_bindgen_futures::JsFuture::from(promise)
.await
.expect("promise should resolve after mark_ready");
}

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@packages/sqlite-web/src/db.rs`:
- Around line 185-210: The wipe_and_recreate implementation resets the request
ID counter causing possible ID reuse between worker generations; instead of
setting *self.next_request_id.borrow_mut() = 1, leave the counter unchanged so
IDs remain monotonic (or advance it safely to a new high value) to avoid
collisions with stale replies from the terminated worker; update
wipe_and_recreate (and any code that initializes next_request_id) to stop
resetting next_request_id and ensure request ID generation logic in the code
that issues requests continues incrementing the existing counter (refer to
next_request_id, pending_queries, wipe_and_recreate, install_onmessage_handler,
and worker).

In `@packages/sqlite-web/src/opfs.rs`:
- Around line 60-75: The function get_directory_if_exists currently treats all
JS errors as "not found"; add the web-sys "DomException" feature in Cargo.toml
and change the error handling in get_directory_if_exists to detect DomException
and only treat a "NotFoundError" as Ok(None), while propagating other errors as
SQLiteWasmDatabaseError (preserving existing describe_js_value-based messages);
keep the creation options via FileSystemGetDirectoryOptions and return
handle.dyn_into().map(Some) on success, but on Err(e) use
e.dyn_ref::<web_sys::DomException>() to inspect .name() and return Ok(None) only
when name == "NotFoundError", otherwise convert the error into an appropriate
SQLiteWasmDatabaseError and return Err.

In `@packages/sqlite-web/src/ready.rs`:
- Around line 173-177: The test currently calls signal.mark_ready() before
retrieving the promise, but mark_ready() consumes/drops the internal promise so
signal.wait_promise() will error; fix by calling let promise =
signal.wait_promise().expect(...) before invoking signal.mark_ready(), then call
signal.mark_ready() and await the previously captured promise with
wasm_bindgen_futures::JsFuture::from(promise).await.expect(...); reference the
existing symbols signal.wait_promise(), signal.mark_ready(), and the promise
variable to locate where to reorder the operations.
- Around line 91-96: reset() currently replaces self.promise without notifying
any existing waiters; if InitializationState is Pending, call the existing
reject (or resolve) callback before dropping it so outstanding promises don't
hang. Specifically, in reset() check whether *self.state.borrow() ==
InitializationState::Pending and if so take() the stored reject (or resolve)
from self.reject/self.resolve and invoke it with a suitable JS error/message
(then drop those callbacks), then proceed to set state to Pending, replace the
promise via create_ready_promise(&self.resolve,&self.reject) and store it in
self.promise; ensure resolve/reject are cleared after invocation to avoid
double-calls.

Comment on lines 60 to 75
async fn get_directory_if_exists(
parent: &FileSystemDirectoryHandle,
name: &str,
) -> Result<Option<FileSystemDirectoryHandle>, SQLiteWasmDatabaseError> {
let options = FileSystemGetDirectoryOptions::new();
options.set_create(false);

match JsFuture::from(parent.get_directory_handle_with_options(name, &options)).await {
Ok(handle) => handle.dyn_into().map(Some).map_err(|e| {
SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
"directory handle type mismatch: {}",
describe_js_value(&e)
))
}),
Err(_) => Ok(None),
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and read the opfs.rs file
find . -name "opfs.rs" -type f

Repository: rainlanguage/sqlite-web

Length of output: 100


🏁 Script executed:

# Check the current content of the file around lines 60-75
cat -n packages/sqlite-web/src/opfs.rs | sed -n '50,85p'

Repository: rainlanguage/sqlite-web

Length of output: 1506


🏁 Script executed:

# Search for DomException usage in the codebase
rg "DomException" --type rust

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Look for similar error handling patterns with NotFoundError or dyn_ref
rg "NotFoundError\|dyn_ref.*DomException" --type rust -A 3

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check the imports at the top of opfs.rs
head -30 packages/sqlite-web/src/opfs.rs

Repository: rainlanguage/sqlite-web

Length of output: 1123


🏁 Script executed:

# Check how get_directory_if_exists is used in the code
rg "get_directory_if_exists" --type rust -B 2 -A 2

Repository: rainlanguage/sqlite-web

Length of output: 665


🏁 Script executed:

# Look at the broader context - check if web_sys has DomException or similar
rg "web_sys::" packages/sqlite-web/src/opfs.rs

Repository: rainlanguage/sqlite-web

Length of output: 273


🏁 Script executed:

# Check what web_sys exports - look for any exception/error handling
grep -r "DomException\|Exception" packages/sqlite-web/Cargo.toml

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check Cargo.toml to understand web_sys version
cat packages/sqlite-web/Cargo.toml | head -50

Repository: rainlanguage/sqlite-web

Length of output: 616


🏁 Script executed:

# Look at other error handling in the file to see patterns
rg "Err\(" packages/sqlite-web/src/opfs.rs -A 1

Repository: rainlanguage/sqlite-web

Length of output: 100


🏁 Script executed:

# Check web-sys version and available exports
cat packages/Cargo.toml | grep -A 10 "web-sys"

Repository: rainlanguage/sqlite-web

Length of output: 118


🏁 Script executed:

# Look for any existing error handling with js_sys or exceptions
rg "js_sys::" packages/sqlite-web/src/ -A 2

Repository: rainlanguage/sqlite-web

Length of output: 16156


🏁 Script executed:

# Search the entire codebase for NotFoundError handling pattern
rg "NotFound" --type rust

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check if there's documentation or examples in the repo about error handling
find . -name "*.rs" -type f -exec grep -l "DomException\|get_directory_handle" {} \;

Repository: rainlanguage/sqlite-web

Length of output: 100


🏁 Script executed:

# Let's check web-sys docs indirectly by looking at what's available
# First, check if there's a Cargo.lock file that shows actual versions
head -100 Cargo.lock | grep -A 5 "name = \"web-sys\""

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check the workspace Cargo.toml to see web-sys configuration
cat Cargo.toml | grep -A 20 "web-sys"

Repository: rainlanguage/sqlite-web

Length of output: 551


🏁 Script executed:

# Look at the errors.rs file to understand error handling patterns
cat -n packages/sqlite-web/src/errors.rs | head -50

Repository: rainlanguage/sqlite-web

Length of output: 1826


🏁 Script executed:

# Check if there's any error name checking pattern in the codebase
rg "name()\|\.name\|\.type" packages/sqlite-web/src/ --type rust

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Search for how File System API errors are handled elsewhere
rg "get_directory_handle\|get_file_handle\|remove_entry" packages/sqlite-web/src/ --type rust -B 2 -A 5

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check web-sys documentation in a comment or see if there's information about DomException
python3 << 'EOF'
# Query web for web_sys DomException availability
import subprocess
result = subprocess.run(['rg', 'DomException', '--type', 'rust'], cwd='.', capture_output=True, text=True)
print("DomException search result (should be empty):", result.returncode)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
EOF

Repository: rainlanguage/sqlite-web

Length of output: 153


🏁 Script executed:

# Let's check if there's a way to check error names in web-sys
# Look for any pattern matching on error types
rg "dyn_ref\|dyn_into" packages/sqlite-web/src/ --type rust -B 1 -A 1

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check web-sys features more comprehensively
rg "DomException" --type rust

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Let's verify if there's an alternative way to check error names
# Search for how errors are handled with Reflect pattern
rg "Reflect::get.*name\|error.*name" --type rust -B 2 -A 2

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check if there are any examples of checking JS error types in the codebase
cat -n packages/sqlite-web/src/opfs.rs | sed -n '1,120p'

Repository: rainlanguage/sqlite-web

Length of output: 4969


🏁 Script executed:

# Verify web-sys version and check if DomException feature exists
web_sys_version=$(rg 'web-sys.*version' Cargo.toml | head -1)
echo "Web-sys configuration: $web_sys_version"

Repository: rainlanguage/sqlite-web

Length of output: 131


🌐 Web query:

web-sys 0.3 DomException feature rust

💡 Result:

In web-sys 0.3, DomException is behind a Cargo feature named DomException. To use web_sys::DomException, enable that feature in your Cargo.toml:

[dependencies.web-sys]
version = "0.3"
features = ["DomException"]

web-sys gates most Web API types behind per-type features, and the docs for DomException explicitly state it requires the DomException feature. [1], [2], [3]


Add DomException feature and distinguish NotFoundError from other errors.

The current implementation swallows all errors with Err(_) => Ok(None), which masks permission issues, type mismatches, and other failures. Only NotFoundError should return Ok(None); other errors must be propagated.

The suggested fix requires adding the DomException feature to web-sys in Cargo.toml since it is not currently enabled. Update the dependencies and implement the error type check in get_directory_if_exists:

🛠️ Required changes

In Cargo.toml, update web-sys features:

web-sys = { version = "0.3", features = [
    "Blob",
    "BlobPropertyBag",
    "Url",
    "Worker",
    "WorkerOptions",
    "WorkerType",
    "BroadcastChannel",
    "MessageEvent",
    "DedicatedWorkerGlobalScope",
    "Navigator",
    "Window",
    "Location",
    "StorageManager",
    "FileSystemDirectoryHandle",
    "FileSystemHandle",
    "FileSystemRemoveOptions",
    "FileSystemGetDirectoryOptions",
    "DomException"
]}

In opfs.rs, update the import:

-use web_sys::{FileSystemDirectoryHandle, FileSystemGetDirectoryOptions, FileSystemRemoveOptions};
+use web_sys::{
+    DomException, FileSystemDirectoryHandle, FileSystemGetDirectoryOptions, FileSystemRemoveOptions,
+};

Update get_directory_if_exists to check error type:

         match JsFuture::from(parent.get_directory_handle_with_options(name, &options)).await {
             Ok(handle) => handle.dyn_into().map(Some).map_err(|e| {
                 SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
                     "directory handle type mismatch: {}",
                     describe_js_value(&e)
                 ))
             }),
-            Err(_) => Ok(None),
+            Err(err) => {
+                if let Some(dom) = err.dyn_ref::<DomException>() {
+                    if dom.name() == "NotFoundError" {
+                        return Ok(None);
+                    }
+                }
+                Err(SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
+                    "failed to access directory {name}: {}",
+                    describe_js_value(&err)
+                )))
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async fn get_directory_if_exists(
parent: &FileSystemDirectoryHandle,
name: &str,
) -> Result<Option<FileSystemDirectoryHandle>, SQLiteWasmDatabaseError> {
let options = FileSystemGetDirectoryOptions::new();
options.set_create(false);
match JsFuture::from(parent.get_directory_handle_with_options(name, &options)).await {
Ok(handle) => handle.dyn_into().map(Some).map_err(|e| {
SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
"directory handle type mismatch: {}",
describe_js_value(&e)
))
}),
Err(_) => Ok(None),
}
async fn get_directory_if_exists(
parent: &FileSystemDirectoryHandle,
name: &str,
) -> Result<Option<FileSystemDirectoryHandle>, SQLiteWasmDatabaseError> {
let options = FileSystemGetDirectoryOptions::new();
options.set_create(false);
match JsFuture::from(parent.get_directory_handle_with_options(name, &options)).await {
Ok(handle) => handle.dyn_into().map(Some).map_err(|e| {
SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
"directory handle type mismatch: {}",
describe_js_value(&e)
))
}),
Err(err) => {
if let Some(dom) = err.dyn_ref::<DomException>() {
if dom.name() == "NotFoundError" {
return Ok(None);
}
}
Err(SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
"failed to access directory {name}: {}",
describe_js_value(&err)
)))
}
}
}
🤖 Prompt for AI Agents
In `@packages/sqlite-web/src/opfs.rs` around lines 60 - 75, The function
get_directory_if_exists currently treats all JS errors as "not found"; add the
web-sys "DomException" feature in Cargo.toml and change the error handling in
get_directory_if_exists to detect DomException and only treat a "NotFoundError"
as Ok(None), while propagating other errors as SQLiteWasmDatabaseError
(preserving existing describe_js_value-based messages); keep the creation
options via FileSystemGetDirectoryOptions and return handle.dyn_into().map(Some)
on success, but on Err(e) use e.dyn_ref::<web_sys::DomException>() to inspect
.name() and return Ok(None) only when name == "NotFoundError", otherwise convert
the error into an appropriate SQLiteWasmDatabaseError and return Err.

- Reject existing promise before dropping to prevent hangs
- Fix test to capture promise before calling mark_ready()
- Add DomException web-sys feature
- Distinguish NotFoundError from other errors in get_directory_if_exists()
- Remove redundant test
- Remove request ID reset to avoid ID collisions with stale replies
- Consolidate wipe tests into single test function
- Add NaN/Infinity parameter validation tests
- Remove redundant tests from tests.rs
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/sqlite-web/src/db.rs`:
- Around line 186-214: wipe_and_recreate currently terminates the worker and
returns early if delete_opfs_sahpool_directory() fails, leaving the instance
without a worker; change the flow to capture the OPFS deletion error (call
delete_opfs_sahpool_directory().await and store any Err), then always proceed to
recreate the worker via create_worker_from_code, install_onmessage_handler,
replace *self.worker, and call self.wait_until_ready().await before finally
propagating the stored deletion error (if any) as the Result; keep mapping
create_worker_from_code errors to SQLiteWasmDatabaseError::JsError and ensure
any pending_queries handling and ready_signal.reset() remain unchanged.

Capture OPFS deletion error and propagate it after worker recreation
to avoid leaving the database in an unusable state.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/sqlite-web/src/db.rs`:
- Around line 333-404: Split the large wasm_bindgen_test function
wipe_and_recreate_tests into focused tests: keep one async test (e.g.,
wipe_and_recreate_happy_path) that exercises SQLiteWasmDatabase::new,
SQLiteWasmDatabase::wipe_and_recreate, creation/insertion/select, repeated wipes
(the t0..t2 loop) and checks on sqlite_master; move the parameter validation
checks that call db.query with NaN/Infinity into a separate async test (e.g.,
query_rejects_invalid_numbers) that only asserts errors for
JsValue::from_f64(NaN/INFINITY/NEG_INFINITY). Ensure both functions retain
#[wasm_bindgen_test(async)] and reuse the same
SQLiteWasmDatabase::new("test_wipe") setup or create distinct named DBs to avoid
interference.

Comment on lines +333 to +404
#[wasm_bindgen_test(async)]
async fn wipe_and_recreate_tests() {
let db = SQLiteWasmDatabase::new("test_wipe").await.unwrap();
db.wipe_and_recreate().await.unwrap();

db.query(
"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)",
None,
)
.await
.unwrap();
db.query("INSERT INTO users (name) VALUES ('Alice')", None)
.await
.unwrap();

let result = db
.query("SELECT COUNT(*) as count FROM users", None)
.await
.unwrap();
assert!(result.contains("\"count\": 1"));

db.wipe_and_recreate().await.unwrap();

let result = db.query("SELECT * FROM users", None).await;
assert!(result.is_err() || result.unwrap().contains("no such table"));

let create_result = db
.query(
"CREATE TABLE new_table (id INTEGER PRIMARY KEY, value TEXT)",
None,
)
.await;
assert!(create_result.is_ok());

let insert_result = db
.query("INSERT INTO new_table (value) VALUES ('test')", None)
.await;
assert!(insert_result.is_ok());

let select_result = db.query("SELECT * FROM new_table", None).await.unwrap();
assert!(select_result.contains("test"));

for i in 0..3 {
db.query(&format!("CREATE TABLE t{} (id INTEGER)", i), None)
.await
.unwrap();
db.wipe_and_recreate().await.unwrap();
}

let result = db
.query("SELECT name FROM sqlite_master WHERE type='table'", None)
.await
.unwrap();
assert!(!result.contains("t0"));
assert!(!result.contains("t1"));
assert!(!result.contains("t2"));

let arr = Array::new();
arr.push(&JsValue::from_f64(f64::NAN));
let res = db.query("SELECT ?", Some(arr)).await;
assert!(res.is_err(), "NaN should be rejected");

let arr = Array::new();
arr.push(&JsValue::from_f64(f64::INFINITY));
let res = db.query("SELECT ?", Some(arr)).await;
assert!(res.is_err(), "+Infinity should be rejected");

let arr = Array::new();
arr.push(&JsValue::from_f64(f64::NEG_INFINITY));
let res = db.query("SELECT ?", Some(arr)).await;
assert!(res.is_err(), "-Infinity should be rejected");
}
Copy link

@coderabbitai coderabbitai bot Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider splitting the test into focused test cases.

This test bundles wipe/recreate scenarios, consecutive wipes, and parameter validation (NaN/Infinity) into a single function. While functional, isolated tests improve debuggability and make failures easier to diagnose. The invalid parameter tests (lines 390-403) are unrelated to wipe_and_recreate and could be a separate test.

🤖 Prompt for AI Agents
In `@packages/sqlite-web/src/db.rs` around lines 333 - 404, Split the large
wasm_bindgen_test function wipe_and_recreate_tests into focused tests: keep one
async test (e.g., wipe_and_recreate_happy_path) that exercises
SQLiteWasmDatabase::new, SQLiteWasmDatabase::wipe_and_recreate,
creation/insertion/select, repeated wipes (the t0..t2 loop) and checks on
sqlite_master; move the parameter validation checks that call db.query with
NaN/Infinity into a separate async test (e.g., query_rejects_invalid_numbers)
that only asserts errors for JsValue::from_f64(NaN/INFINITY/NEG_INFINITY).
Ensure both functions retain #[wasm_bindgen_test(async)] and reuse the same
SQLiteWasmDatabase::new("test_wipe") setup or create distinct named DBs to avoid
interference.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have opted to use a single test because the database persists between the tests and it was causing us issues. By doing it this way, we make sure that we have we are testing everything properly.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@findolor, thank you for the clarification! That makes perfect sense—if the database state persists between test functions in your WASM test environment, consolidating the related scenarios into a single test is the right approach to ensure proper isolation and test correctness. I appreciate you explaining the reasoning behind this design decision.


✏️ Learnings added
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 27
File: packages/sqlite-web/src/db.rs:333-404
Timestamp: 2026-01-27T10:13:33.826Z
Learning: In the sqlite-web WASM test environment, database state can persist between test functions, causing test interference. To ensure proper test isolation, related test scenarios for features like wipe_and_recreate should be consolidated into a single test function rather than split into multiple independent test cases.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/lib.rs : Expose an async, promise-based query interface from the SQLiteWasmDatabase public API

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 14
File: svelte-test/tests/database-functions/float-is-zero.test.ts:39-57
Timestamp: 2025-09-19T07:06:20.173Z
Learning: PRAGMA function_list is not available in the WASM SQLite test environment used by svelte-test, so availability testing should rely on direct function calls rather than PRAGMA queries.

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/database_functions.rs : Integrate and expose rain.math.float-backed custom database functions for SQLite queries

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/worker_template.rs : Ensure the worker JavaScript generated by worker_template.rs embeds the WASM (base64) and does not fetch external WASM files

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 9
File: packages/sqlite-web-core/src/coordination.rs:52-65
Timestamp: 2025-09-15T06:11:31.781Z
Learning: In packages/sqlite-web-core/src/coordination.rs, the pattern of using `db.borrow_mut().take()` followed by async database operations and then putting the database back with `*db.borrow_mut() = Some(database)` is safe in their use case and doesn't cause concurrent access issues or "Database not initialized" errors.

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/worker.rs : Keep worker.rs as the main worker entry point invoked by worker_main()

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 6
File: svelte-test/tests/database-functions/float-sum.test.ts:78-86
Timestamp: 2025-09-17T08:04:44.062Z
Learning: In svelte-test/tests/database-functions/float-sum.test.ts, the schema defines `amount TEXT NOT NULL` but the NULL value handling tests attempt to insert NULL values, creating a constraint violation that prevents the tests from passing.

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 15
File: svelte-test/tests/integration/error-handling.test.ts:418-434
Timestamp: 2025-09-17T17:44:54.855Z
Learning: In the sqlite-web test harness, parameter binding is not available for the db.query() method, so string interpolation with manual SQL escaping using value.replace(/'/g, "''") is the necessary approach for handling special characters in test data.

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 5
File: packages/sqlite-web-core/src/database_functions/bigint_sum.rs:76-93
Timestamp: 2025-08-27T05:55:40.481Z
Learning: In the sqlite-web-core codebase, for SQLite aggregate function context initialization, the maintainer prefers using byte-scanning to detect zero-initialized memory rather than using explicit initialization flags or Option wrappers.

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/messages.rs : Use structured message types for worker communication as defined in messages.rs

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 12
File: svelte-test/tests/fixtures/test-helpers.ts:11-11
Timestamp: 2025-09-15T10:01:17.743Z
Learning: In the sqlite-web project, test database names in createTestDatabase function do not need timestamp suffixes for uniqueness. The maintainer has confirmed that test isolation via unique naming is not required.

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/embedded_worker.js : Do not manually edit embedded_worker.js; it is generated by the build and should be treated as read-only

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Maintain self-contained workers with no external WASM file dependencies across the project

Resolve conflict in pkg/package.json by accepting main's standard semver
versioning and repository field.
@findolor findolor merged commit 292f587 into main Jan 28, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants